package com.link2room.aeriabots.facade.impl;

import com.link2room.aeriabots.constant.CallBackType;
import com.link2room.aeriabots.constant.WXConstant;
import com.link2room.aeriabots.dto.callBack.WxCallBackDto;
import com.link2room.aeriabots.dto.param.inside.WxQrcodeInsideParamDto;
import com.link2room.aeriabots.entity.*;
import com.link2room.aeriabots.facade.IWxCallBackFacadeService;
import com.link2room.aeriabots.service.IWXQrcodeService;
import com.link2room.aeriabots.thread.task.AwardSalesmanMemberTask;
import com.link2room.aeriabots.thread.util.ThreadUtil;
import com.link2room.blaster.constant.BlasterMnsConstant;
import com.link2room.common.jpa.result.QueryResult;
import com.link2room.common.mns.MNSUtil;
import com.link2room.common.service.ICommonService;
import com.link2room.common.service.ILink2RoomApiBaseService;
import com.link2room.common.util.L2RAssert;
import com.link2room.common.util.LogUtil;
import com.link2room.common.util.StringUtil;
import com.link2room.common.util.XmlUtil;
import com.link2room.rodimus.constant.MemberConstant;
import com.link2room.rodimus.constant.SalesmanSubScanScriberTypeConstant;
import com.link2room.rodimus.dto.param.base.SalesmanAwardMemberParamDto;
import com.link2room.rodimus.entity.MemberBase;
import com.link2room.rodimus.service.IMemberService;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import org.apache.juli.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * Created by LN on 2016/9/18.
 */
@Component("wxCallBackFacadeService")
public class WxCallBackFacadeServiceImpl extends BaseFacade implements IWxCallBackFacadeService {

    private static Logger logger = LoggerFactory.getLogger(WxCallBackFacadeServiceImpl.class);
    private ExecutorService exe = Executors.newCachedThreadPool();
    @Resource
    private MNSUtil mnsUtil;
    @Resource
    private ICommonService commonServiceSupport;
    @Resource
    private IWXQrcodeService wxQrcodeService;

    @Resource
    private IMemberService memberService;

    @Resource
    private ILink2RoomApiBaseService link2RoomApiBaseService;


    private String geSubScanScriberType(String lhotelGroupCode) throws Exception {
        return link2RoomApiBaseService.getSysOption(null, SalesmanSubScanScriberTypeConstant.sys_cate,lhotelGroupCode);
    }
    @Override
    public String wxCallBackHandller(String xmlStr) throws Exception {
//        String xmlStr = this.wxBizMsgCryptDecryptMsg(msgSignature,timeStamp,nonce,postData);
        String replayStr = null;
        String resStr = null;
        WxCallBackDto dto = XmlUtil.fromXML(xmlStr, WxCallBackDto.class);
        //扫码计数
        scanCount(dto);
        WxCallBackDto replayDto = null;
        //判断回调类型
        //模板消息发送
        if (CallBackType.MSG_TYPE.EVENT.equals(dto.getMsgType()) && CallBackType.EVENT_TYPE.TEMPLATE_SEND_JOB_FINISH.equals(dto.getEvent())) {
            //发送log到消息队列
            mnsUtil.putMessageToQueue(BlasterMnsConstant.QUEUE_MSG_LOG, xmlStr);
        } else if (CallBackType.MSG_TYPE.EVENT.equals(dto.getMsgType()) && CallBackType.EVENT_TYPE.SUBSCRIBE.equals(dto.getEvent())) {
            //扫码关注
            if (StringUtil.isNotBlank(dto.getTicket())) {
                logger.info("用户关注 "+ SalesmanSubScanScriberTypeConstant.Type.MEMBER+ " " + dto.getFromUserName() + ","+dto.getToUserName());
                WxCertificate wc = getWxCertificateByAliasName(dto.getToUserName());
                String subScanScriberType = geSubScanScriberType(wc.getLhotelGroupCode());
                if(SalesmanSubScanScriberTypeConstant.Type.MEMBER.equals(subScanScriberType)){
                    logger.info("会员关注模式-lhotelGroupCode "+ SalesmanSubScanScriberTypeConstant.Type.MEMBER+" " +wc.getLhotelGroupCode() + " " + dto.getFromUserName() + ","+dto.getToUserName());
                    replayDto = this.memberScanSriber(dto);
                }else {
                    logger.info("粉丝关注模式-lhotelGroupCode "+ SalesmanSubScanScriberTypeConstant.Type.WX_FANS+" " +wc.getLhotelGroupCode() + " " + dto.getFromUserName() + ","+dto.getToUserName());
                    replayDto = this.subScanScriber(dto);
                }
            } else {
                //普通关注
                replayDto = this.subScriber(dto);
            }
        } else if (CallBackType.MSG_TYPE.EVENT.equals(dto.getMsgType()) && CallBackType.EVENT_TYPE.UNSUBSCRIBE.equals(dto.getEvent())) {
            //取消关注
            String openid = dto.getFromUserName();
            this.unSubScriber(openid);
        } else if (CallBackType.MSG_TYPE.EVENT.equals(dto.getMsgType()) && CallBackType.EVENT_TYPE.SCAN.equals(dto.getEvent())) {
            //扫码事件
            String ticket = dto.getTicket();
            replayDto = repltScan(dto.getToUserName(), ticket,dto);
        } else if (CallBackType.MSG_TYPE.TEXT.equals(dto.getMsgType())) {
            //关键字回复
            String keyWord = dto.getContent();
            if (StringUtil.isBlank(keyWord)) {
                return null;
            }
            replayDto = this.replyKeyWord(dto.getToUserName(), keyWord);

        }
        if (replayDto != null) {
            replayDto.setCreateTime(new Long(new Date().getTime()).toString().substring(0, 8));
            replayDto.setToUserName(dto.getFromUserName());
            replayDto.setFromUserName(dto.getToUserName());
            WxCallBackDto resDto = null;
            replayStr = XmlUtil.toXML(replayDto);
            System.out.println(replayStr);
//            resStr = XmlUtil.toMarshalL(result);
            String time = replayDto.getCreateTime();
            WXBizMsgCrypt.EncryptBody ncryptBody = this.wxBizMsgCryptEncryptBody(replayStr, time, "nonc");
            resDto = new WxCallBackDto();
            resDto.setEncrypt(ncryptBody.getEncryptMsg());
            resDto.setNonce(ncryptBody.getNonce());
            resDto.setTimestamp(ncryptBody.getTimeStamp());
            resDto.setMsgSignature(ncryptBody.getSignature());
            resStr = ncryptBody.getEncryptMsg();
//            resStr = XmlUtil.toXML(result);
            LogUtil.logger.debug(resStr);
        }
        return resStr;
    }

    @Override
    public String wxMsgCryptDecryptMsg(String msgSignature, String timeStamp, String nonce, String postData) throws Exception {
        String xmlStr = this.wxBizMsgCryptDecryptMsg(msgSignature, timeStamp, nonce, postData);
        return xmlStr;
    }


    /**
     * 扫码回复
     *
     * @param appAliasName
     * @param ticket
     * @return
     * @throws Exception
     */
    private WxCallBackDto repltScan(String appAliasName, String ticket,WxCallBackDto dto) throws Exception {
        if (StringUtil.isBlank(ticket)) {
            return null;
        }
        WxQrcode find = new WxQrcode();
        find.setTicket(ticket);
        QueryResult<WxQrcode> res = commonServiceSupport.findAllEQ(WxQrcode.class, find, 1, 1);
        if (res == null || res.getResultlist() == null || res.getResultlist().size() == 0) {
            return null;
        }
        WxQrcode qrcode = res.getResultlist().get(0);
        String qrType = qrcode.getQrType();
        if (WXConstant.WX_QRCODE.TYPE.REPLY.equals(qrType)) {
            String keyValue = qrcode.getSceneId() != null ? qrcode.getSceneId().toString() : null;
            if (StringUtil.isBlank(keyValue)) {
                return null;
            }
            return this.replyScan(appAliasName, WxReplyKeyMapping.KeyType.QRCODE, keyValue);
        } else if(WXConstant.WX_QRCODE.TYPE.SALESMAN.equals(qrType)) {
            WxCertificate wc = getWxCertificateByAliasName(appAliasName);
            String subScanScriberType = geSubScanScriberType(wc.getLhotelGroupCode());
            if(SalesmanSubScanScriberTypeConstant.Type.MEMBER.equals(subScanScriberType)){
                return this.memberScanSriber(dto);
            }
        }
        return null;
    }


    /**
     * 关键字回复
     * 模糊匹配规则: 关键字 A ， 传入 words 包含 A 则 匹配成功。 eg： 关键字：吃饭。 传入：你吃饭了吗？ 结果：匹配成功。
     *
     * @param appAliasName
     * @param keyValue
     * @return
     * @throws Exception
     */
    private WxCallBackDto replyKeyWord(String appAliasName, String keyValue) throws Exception {
        if (StringUtil.isBlank(keyValue)) {
            return null;
        }
        WxCertificate wc = getWxCertificateByAliasName(appAliasName);
        String lhotelGroupCode = wc.getLhotelGroupCode();
        //查找出所有关键字mapping
        WxReplyKeyMapping find = new WxReplyKeyMapping();
        find.setLhotelGroupCode(lhotelGroupCode);
        find.setKeyType(WxReplyKeyMapping.KeyType.TEXT);
        QueryResult<WxReplyKeyMapping> res = commonServiceSupport.findAllEQ(WxReplyKeyMapping.class, find);
        if (res != null && res.getResultlist() != null && res.getResultlist().size() > 0) {
            //过滤关键字:
            Optional<WxReplyKeyMapping> mappingOptional = res.getResultlist().stream()
                    .filter(
                            (r) -> StringUtil.isNotBlank(r.getKeyValue())
                                    && keyValue.contains(r.getKeyValue())
                    )
                    .findFirst();
            if (!mappingOptional.isPresent()) {
                return null;
            }
            WxReplyKeyMapping mappingEntity = mappingOptional.get();
            String templateId = mappingEntity.getTemplateId();
            L2RAssert.stringBlank(templateId, "[templateId] is null in " + mappingEntity.toString());
            return this.replyTemplate(lhotelGroupCode, templateId);
        } else {
            return null;
        }
    }

    /**
     * 回复 key
     *
     * @param appAliasName
     * @param keyType
     * @param keyValue
     * @return
     * @throws Exception
     */
    private WxCallBackDto replyScan(String appAliasName, String keyType, String keyValue) throws Exception {
        WxCertificate wc = getWxCertificateByAliasName(appAliasName);
        String lhotelGroupCode = wc.getLhotelGroupCode();
        WxReplyKeyMapping find = new WxReplyKeyMapping();
        find.setLhotelGroupCode(lhotelGroupCode);
        find.setKeyType(keyType);
        find.setKeyValue(keyValue);
        QueryResult<WxReplyKeyMapping> res = commonServiceSupport.findAllEQ(WxReplyKeyMapping.class, find);
        if (res != null && res.getResultlist() != null && res.getResultlist().size() > 0) {
            WxReplyKeyMapping mappingEntity = res.getResultlist().get(0);
            String templateId = mappingEntity.getTemplateId();
            L2RAssert.stringBlank(templateId, "[templateId] is null in " + mappingEntity.toString());
            return this.replyTemplate(lhotelGroupCode, templateId);
        } else {
            return null;
        }
    }


    /**
     * 图文回复
     *
     * @param lhotelGroupCode
     * @param templateId
     * @return
     * @throws Exception
     */
    private WxCallBackDto replyTemplate(String lhotelGroupCode, String templateId) throws Exception {


        //查找回复内容
        WxReplyTemplate find = new WxReplyTemplate();
        find.setLhotelGroupCode(lhotelGroupCode);
        find.setTemplateId(templateId);
        find.setIsHalt("F");
        QueryResult<WxReplyTemplate> queryRes = commonServiceSupport.findAllEQ(WxReplyTemplate.class, find, 1, 1);
        if (queryRes != null && queryRes.getTotalrecord() > 0) {

            WxReplyTemplate wxReplyKeyEntity = queryRes.getResultlist().get(0);
            if (WxReplyTemplate._MsgType.news.toString().equals(wxReplyKeyEntity.getMsgType())) {
                WxCallBackDto xmlDto = new WxCallBackDto();

                xmlDto.setMsgType(WxCallBackDto.MsgType.NEWS);
                WxReplyTemplateDetail findArticle = new WxReplyTemplateDetail();
                findArticle.setTemplateId(templateId);
                findArticle.setLhotelGroupCode(lhotelGroupCode);
                findArticle.setIsHalt("F");
                QueryResult<WxReplyTemplateDetail> queryArticleRes = commonServiceSupport.findAllEQ(WxReplyTemplateDetail.class, findArticle, null, null);
                if (queryArticleRes != null && queryArticleRes.getTotalrecord() > 0) {
                    List<WxReplyTemplateDetail> articleEntities = queryArticleRes.getResultlist();
                    List<WxCallBackDto.Article> articleList = articleEntities.stream()
                            .sorted(Comparator.comparing(WxReplyTemplateDetail::getListOrder))
                            .map(articleEntity -> articleEntityToArticle(articleEntity))
                            .collect(Collectors.toList());
                    xmlDto.setArticles(articleList);
                    xmlDto.setArticleCount(articleList.size());
                }
                return xmlDto;
            }
        }
        return null;
    }


    private WxCallBackDto.Article articleEntityToArticle(WxReplyTemplateDetail entity) {
        if (entity != null) {
            WxCallBackDto.Article article = new WxCallBackDto.Article();
            article.setDescription(entity.getDescription());
            article.setPicUrl(entity.getPicUrl());
            article.setTitel(entity.getTitle());
            article.setUrl(entity.getUrl());
            return article;
        } else {
            return null;
        }
    }

    /**
     * 普通关注
     */
    private WxCallBackDto subScriber(WxCallBackDto dto) throws Exception {
        String openid = dto.getFromUserName();
        WxCertificate wx = getWxCertificateByAliasName(dto.getToUserName());
        String lhotelGroupCode = wx.getLhotelGroupCode();
        //检查是否关注过，并更新wxFans
        WxFans wxFansCon = new WxFans();
        wxFansCon.setOpenid(openid);
        WxFans fans = commonServiceSupport.findOneEQ(WxFans.class, wxFansCon);
        if (fans == null) {
            wxFansCon.setLhotelGroupCode(lhotelGroupCode);
            wxFansCon.setSubNum(1);
            wxFansCon.setIsSubscribe("T");
            commonServiceSupport.save(wxFansCon);
            //if 没有关注过 推送 粉丝
            // 发送给粉丝注册页面链接
            WxCallBackDto resDto = new WxCallBackDto();
            resDto.setToUserName(dto.getFromUserName());
            resDto.setFromUserName(dto.getToUserName());
            resDto.setMsgType(WxCallBackDto.MsgType.TEXT);
            resDto.setCreateTime(new Long(new Date().getTime()).toString().substring(0, 8));
            resDto.setContent("加入我们,成为我们的会员:\n" + getWxRegisterUrl(lhotelGroupCode) + lhotelGroupCode);
            return resDto;
        } else {
            fans.setSubNum(fans.getSubNum() + 1);
            fans.setIsSubscribe("T");
            commonServiceSupport.merge(fans);
            WxCallBackDto resDto = new WxCallBackDto();
            resDto.setToUserName(dto.getFromUserName());
            resDto.setFromUserName(dto.getToUserName());
            resDto.setCreateTime(new Long(new Date().getTime()).toString().substring(0, 8));
            resDto.setMsgType(WxCallBackDto.MsgType.TEXT);
            resDto.setContent("欢迎再次关注我们");
            return resDto;
        }
    }

    /**
     * 扫码后.非会员推送注册页面或者金卡页面.
     * @param dto
     * @return
     */
    private WxCallBackDto memberScanSriber(WxCallBackDto dto) throws Exception {
        WxCertificate wc = getWxCertificateByAliasName(dto.getToUserName());
        WxCallBackDto resDto = new WxCallBackDto();

        resDto.setToUserName(dto.getFromUserName());
        resDto.setFromUserName(dto.getToUserName());
        resDto.setMsgType(WxCallBackDto.MsgType.TEXT);
        resDto.setCreateTime(new Long(new Date().getTime()).toString().substring(0, 8));
        String openid = dto.getFromUserName();
        MemberBase member = memberService.selectMemberByOpenid(null, openid);
        if(member==null){
            //非会员推送注册页面
            String regUrl = "加入我们,成为我们的会员:\n" + getWxRegisterUrl(wc.getLhotelGroupCode()) + wc.getLhotelGroupCode();
            resDto.setContent(regUrl);
        }else if(!MemberConstant.MEMBER_LEVEL.L2.equals(member.getMemberLevel())){
            //非金卡推送金卡页面.
            logger.info("金卡升级页url查询-lhotelGroupCode "+ wc.getLhotelGroupCode() + " " + dto.getFromUserName() + ","+dto.getToUserName());
            String regUrl = "金卡升级页面:\n" +   getLevelUpRrl(wc.getLhotelGroupCode()) + wc.getLhotelGroupCode();
            resDto.setContent(regUrl);
        }else {
            //谢谢关注
            resDto.setContent("欢迎再次关注我们");
        }
        return resDto;
    }


    /**
     * 粉丝扫码关注处理
     */
    private WxCallBackDto subScanScriber(WxCallBackDto dto) throws Exception {
        String openid = dto.getFromUserName();
        String ticket = dto.getTicket();
        WxCertificate wc = getWxCertificateByAliasName(dto.getToUserName());
        //获得关注场景
        WxQrcode qCon = new WxQrcode();
        qCon.setTicket(ticket);
        String sceneStr = "";
        String regUrl = "加入我们,成为我们的会员:\n" + getWxRegisterUrl(wc.getLhotelGroupCode()) + wc.getLhotelGroupCode();
        ;
        WxQrcode wxQrcode = commonServiceSupport.findOneEQ(WxQrcode.class, qCon);

        if (wxQrcode == null) {
            LogUtil.logger.info("粉丝扫码关注时，[ticket]=" + ticket + ",查询没有结果.");
        } else {
            sceneStr = WxQrcodeInsideParamDto.getScenStr(wxQrcode.getQrType(), wxQrcode.getQrkey());
        }
        //检查是否关注过，并更新wxFans
        WxFans wxFansCon = new WxFans();
        wxFansCon.setOpenid(openid);
        WxFans fans = commonServiceSupport.findOneEQ(WxFans.class, wxFansCon);
        if (fans == null) {
            wxFansCon.setLhotelGroupCode(wc.getLhotelGroupCode());
            wxFansCon.setSceneStr(sceneStr);
            wxFansCon.setSubNum(1);
            wxFansCon.setIsSubscribe("T");
            commonServiceSupport.save(wxFansCon);
            //if 没有关注过 推送 粉丝
            // 发送给粉丝注册页面链接
            WxCallBackDto resDto = new WxCallBackDto();
            resDto.setToUserName(dto.getFromUserName());
            resDto.setFromUserName(dto.getToUserName());
            resDto.setMsgType(WxCallBackDto.MsgType.TEXT);
            resDto.setCreateTime(new Long(new Date().getTime()).toString().substring(0, 8));

            //result.setContent("这里是注册链接~~~~~++++++++");
            resDto.setContent(regUrl);
            if (wxQrcode != null && WXConstant.WX_QRCODE.TYPE.SALESMAN.equals(wxQrcode.getQrType())) {
                //通知粉丝关注，salesMan
                SalesmanAwardMemberParamDto paramDto = new SalesmanAwardMemberParamDto();
                paramDto.setSalesmanNo(wxQrcode.getQrkey());
                paramDto.setOpenId(openid);
                paramDto.setLhotelGroupCode(wxQrcode.getLhotelGroupCode());
                ThreadUtil.getExecutor().submit(new AwardSalesmanMemberTask(paramDto));
            }
            return resDto;
        } else {
            fans.setSubNum(fans.getSubNum() + 1);
            fans.setIsSubscribe("T");
            commonServiceSupport.merge(fans);
            WxCallBackDto resDto = new WxCallBackDto();
            resDto.setToUserName(dto.getFromUserName());
            resDto.setFromUserName(dto.getToUserName());
            resDto.setCreateTime(new Long(new Date().getTime()).toString().substring(0, 8));
            resDto.setMsgType(WxCallBackDto.MsgType.TEXT);
            resDto.setContent("欢迎再次关注我们");
            return resDto;
        }
    }

    /**
     * 取消关注
     *
     * @param openid
     * @throws Exception
     */
    private void unSubScriber(String openid) throws Exception {
        //检查是否关注过，并更新wxFans
        WxFans wxFansCon = new WxFans();
        wxFansCon.setOpenid(openid);
        WxFans fans = commonServiceSupport.findOneEQ(WxFans.class, wxFansCon);
        if (fans != null) {
            fans.setIsSubscribe("F");
            commonServiceSupport.merge(fans);
        }
    }

    private void scanCount(WxCallBackDto dto) {
        String ticket = dto.getTicket();
        if (!CallBackType.MSG_TYPE.EVENT.equals(dto.getMsgType()) || StringUtil.isBlank(ticket)) {
            return;
        }
        exe.submit(() -> {
            WxQrcode qCon = new WxQrcode();
            qCon.setTicket(ticket);
            WxQrcode wxQrcode = null;
            try {
                wxQrcode = commonServiceSupport.findOneEQ(WxQrcode.class, qCon);
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
            if (wxQrcode == null) {
                return;
            }
            String qrType = wxQrcode.getQrType();
            String qrKey = wxQrcode.getQrkey();
            String lhotelGroupCode = wxQrcode.getLhotelGroupCode();
            try {
                wxQrcodeService.scanCount(lhotelGroupCode, qrType, qrKey, dto.getEvent(), dto.getFromUserName(),dto.getMsgId());
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }


}
